﻿using System;
using System.Linq;
using System.Numerics;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using psieve;
using psieve.factor;
using psieve.sieve;

namespace psieveTest
{
    [TestClass]
    public class TestBigInteger
    {
        private readonly Random random = new Random();

        private BigInteger Generate(int len)
        {
            return Utils.GenerateRandomByDigits(len);
        }

        private void TestSquare(BigInteger n)
        {
            Console.WriteLine("testing perfect square: " + n);

            BigInteger s = (n * n).Sqrt();

            Assert.AreEqual(n, s);
        }

        private void TestArb(BigInteger n)
        {
            Console.WriteLine("testing: " + n);

            BigInteger sqrt = n.Sqrt();

            Console.WriteLine("sqrt: " + sqrt);

            BigInteger sqrt1 = sqrt + 1;

            Assert.IsTrue(sqrt * sqrt <= n && sqrt1 * sqrt1 > n);
        }

        [TestMethod]
        public void TestSqrt()
        {
            BigInteger a = 121;
            Assert.AreEqual(11, a.Sqrt());
        }

        [TestMethod]
        public void TestSqrt10()
        {
            BigInteger n = Generate(10);
            TestSquare(n);
            TestArb(n);
        }

        [TestMethod]
        public void TestSqrt20()
        {
            BigInteger n = Generate(20);
            TestSquare(n);
            TestArb(n);
        }

        [TestMethod]
        public void TestSqrt50()
        {
            BigInteger n = Generate(50);
            TestSquare(n);
            TestArb(n);
        }

        [TestMethod]
        public void TestSqrt100()
        {
            BigInteger n = Generate(100);
            TestSquare(n);
            TestArb(n);
        }

        [TestMethod]
        public void TestSqrt500()
        {
            BigInteger n = Generate(500);
            TestSquare(n);
            TestArb(n);
        }

        [TestMethod]
        public void TestBitLength()
        {
            Assert.AreEqual(1, new BigInteger(0).BitLength());
            Assert.AreEqual(1, new BigInteger(1).BitLength());

            Assert.AreEqual(2, new BigInteger(2).BitLength());
            Assert.AreEqual(2, new BigInteger(3).BitLength());

            Assert.AreEqual(3, new BigInteger(4).BitLength());

            for (int x = 2; x <= 65; x++)
            {
                BigInteger n = BigInteger.One << x;

                Console.WriteLine("n: " + n);

                Assert.AreEqual(x, (n - 1).BitLength());
                Assert.AreEqual(x + 1, n.BitLength());
                Assert.AreEqual(x + 1, (n + 1).BitLength());
            }
        }

        [TestMethod]
        public void TestRandomBits()
        {
            for (int x = 1; x <= 1000; x++)
            {
                BigInteger n = Utils.GenerateRandomByBits(x);
                Assert.AreEqual(x, n.BitLength());
            }
        }

        [TestMethod]
        public void TestProbablyPrime()
        {
            int bits = 50;
            BigInteger a = Utils.GenerateProbablyPrimeByBits(bits, 1);

            Console.WriteLine("a: " + a);

            IFactor fac = new TrialDivision(new Eratosthenes());

            BigInteger f = fac.Factor(a).Single();

            Assert.AreEqual(a, f);
        }
    }
}
